package cn.com.libertymutual.sp.service.impl;

import java.io.IOException;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;

import cn.com.libertymutual.core.base.dto.MessageResponseDto;
import cn.com.libertymutual.core.redis.util.RedisUtils;
import cn.com.libertymutual.core.util.Constants;
import cn.com.libertymutual.core.util.DateUtil;
import cn.com.libertymutual.core.util.enums.CoreServiceEnum;
import cn.com.libertymutual.core.web.ServiceResult;
import cn.com.libertymutual.core.web.util.RequestUtils;
import cn.com.libertymutual.sp.bean.TbSpSaleLog;
import cn.com.libertymutual.sp.dao.SpSaleLogDao;
import cn.com.libertymutual.sp.service.api.SmsService;
import cn.com.libertymutual.sys.bean.SysServiceInfo;

@Service("SmsService")
public class SmsImpl implements SmsService {
	private Logger log = LoggerFactory.getLogger(getClass());
	@Resource
	private RestTemplate restTemplate;
	@Resource
	private RedisUtils redis;
	@Autowired
	private SpSaleLogDao spSaleLogDao;

	@Override
	public ServiceResult verifyCode(HttpServletRequest request, ServiceResult sr, RedisUtils redis, String inCode, String mobile) throws Exception {
		return verify(request, sr, redis, inCode, mobile);
	}

	@Override
	public boolean verifyCodeBool(HttpServletRequest request, ServiceResult sr, RedisUtils redis, String inCode, String mobile) throws Exception {
		sr = verify(request, sr, redis, inCode, mobile);
		if (sr.getState() == ServiceResult.STATE_SUCCESS) {
			return true;
		}
		return false;
	}

	/**Remarks: 校验短信验证码<br>
	 * Version：1.0<br>
	 * Author：AoYi<br>
	 * DateTime：2017年11月13日下午6:04:53<br>
	 * Project：liberty_sale_plat<br>
	 * @param request
	 * @param sr
	 * @param redis
	 * @param inCode
	 * @param mobile
	 * @return
	 * @throws ParseException
	 */
	private ServiceResult verify(HttpServletRequest request, ServiceResult sr, RedisUtils redis, String inCode, String mobile) throws ParseException {
		sr.setAppFail(Constants.SYS_ERROR_MSG);

		if (StringUtils.isBlank(inCode)) {
			sr.setAppFail("短信验证码为空");
			return sr;
		}
		if (inCode.equals(Constants.REDIS_PHONE_VERIFY_CODE_SUPER)) {
			sr.setResult("验证码正确");
			sr.setSuccess();
			return sr;
		}
		if (!inCode.trim().matches(Constants.REGEX_NUMBER_CODE)) {
			sr.setAppFail("短信验证码为" + Constants.REGEX_NUMBER_CODE_LENGTH + "位数字");
			return sr;
		}
		// 校验手机号是否有效
		sr = this.phoneNumCheck(sr, mobile);
		if (sr.getState() != ServiceResult.STATE_SUCCESS) {
			return sr;
		}
		sr.setAppFail("发送短信失败");
		// 获取缓存中的验证码
		Object object = redis.get(request.getSession().getId(), Constants.REDIS_PHONE_VERIFY_CODE);
		if (object == null) {
			sr.setAppFail("短信验证码过期,请重新获取");
			return sr;
		}
		@SuppressWarnings("unchecked")
		HashMap<String, String> verifyCode = (HashMap<String, String>) object;
		String storeMobile = verifyCode.get("mobile");
		String storeCode = verifyCode.get("code");
		String sendTime = verifyCode.get("time");
		// 校验手机号是否有效
		sr = this.phoneNumCheck(sr, storeMobile);
		if (sr.getState() != ServiceResult.STATE_SUCCESS) {
			return sr;
		}
		sr.setAppFail("发送短信失败");
		if (StringUtils.isBlank(storeCode) || !storeCode.trim().matches(Constants.REGEX_NUMBER_CODE)) {
			sr.setAppFail("验证码格式错误");
			return sr;// 验证码校验格式
		}
		if (StringUtils.isBlank(sendTime) || !sendTime.trim().matches(Constants.REGEX_DATE_FORMART2)) {
			sr.setAppFail("验证码时间格式错误");
			return sr;// 验证码时间校验格式
		}
		Long oldTime = new SimpleDateFormat(DateUtil.DATE_TIME_PATTERN2LANG).parse(sendTime).getTime();
		Long overTime = System.currentTimeMillis() - oldTime;// 剩余时间
		// 验证码过期,1秒=1000毫秒
		if (overTime >= Constants.SMS_VALIDATE_OUTTIME * 1000) {
			sr.setAppFail("短信验证码已超时");
			redis.deleteWithPrefix(request.getSession().getId(), Constants.REDIS_PHONE_VERIFY_CODE);
			return sr;
		}
		if (!storeMobile.trim().equals(mobile.trim())) {
			sr.setAppFail("该号码未发送验证码");
			return sr;
		}
		if (storeCode.trim().equals(inCode.trim()) && storeMobile.trim().equals(mobile.trim())) {
			// 状态更新后清除验证码
			redis.deleteWithPrefix(request.getSession().getId(), Constants.REDIS_PHONE_VERIFY_CODE);
			sr.setResult("短信验证码正确");
			sr.setSuccess();
		} else {
			sr.setAppFail("验证码错误");
		}
		return sr;
	}

	@Override
	public ServiceResult smsToMobile(String mobile, String contents) {
		ServiceResult sr = new ServiceResult(Constants.SYS_ERROR_MSG, ServiceResult.STATE_APP_EXCEPTION);
		try {
			sr = this.createSmsToMobile(null, sr, mobile, contents, null);
		} catch (IOException | ParseException e) {
			e.printStackTrace();
		}
		return sr;
	}

	@Transactional(propagation = Propagation.REQUIRED) // 事务
	@Modifying(clearAutomatically = true) // @Modifying注解需要使用clearAutomatically=true，同一接口更新后立即查询获得更新后的数据,默认false查询还是更新前的数据
	@Override
	public ServiceResult createSmsToMobile(HttpServletRequest request, ServiceResult sr, String mobile, String contents, String typeName)
			throws IOException, ParseException {
		sr.setAppFail("发送短信失败");

		boolean isSendCode = true;// 默认发送验证码
		if (StringUtils.isNotBlank(contents)) {
			isSendCode = false;// 发送其他信息
		}
		// 校验手机号是否有效
		sr = this.phoneNumCheck(sr, mobile);
		if (sr.getState() != ServiceResult.STATE_SUCCESS) {
			log.info("SMS：{}", JSON.toJSONString(sr));
			return sr;
		}
		if (isSendCode) {
			sr.setAppFail("验证码发送失败");

			if (StringUtils.isBlank(typeName)) {
				sr.setResult("请提供操作名称");
				return sr;
			}
			Pattern p = Pattern.compile(Constants.REGEX_SPECIALCHARACTERS_CODE);
			Matcher m = p.matcher(typeName);
			if (m.find()) {
				sr.setResult("您提供的操作名称含有特殊字符");
				return sr;
			}
			// 获取缓存中的验证码
			Object object = redis.get(request.getSession().getId(), Constants.REDIS_PHONE_VERIFY_CODE);
			if (object != null) {
				@SuppressWarnings("unchecked")
				HashMap<String, String> verifyCode = (HashMap<String, String>) object;
				String storeMobile = verifyCode.get("mobile");
				String sendTime = verifyCode.get("time");
				Long oldTime = new SimpleDateFormat(DateUtil.DATE_TIME_PATTERN2LANG).parse(sendTime).getTime();
				Long overTime = System.currentTimeMillis() - oldTime;// 剩余时间
				// 验证码有效,校验同一手机倒计时内仅发送1次
				if (mobile.equals(storeMobile) && overTime < Constants.SMS_VALIDATE_OUTTIME * 1000) {
					sr.setResult("手机号[" + Constants.SMS_VALIDATE_OUTTIME + "]秒内只能获取一次");
					log.info("SMS：{}", JSON.toJSONString(sr));
					return sr;
				}
			}
		}
		sr.setAppFail("发送短信失败");
		/**
		 * ====================
		 * 发送操作
		 * ====================
		 */
		StringBuffer buffer = new StringBuffer();
		String systemSource = "LibertySALE";
		long timestamp = new Date().getTime();
		String code = this.createCode();// 创建随机验证码
		contents = isSendCode ? ("您正在" + typeName + "，验证码是：" + code + "，切勿告知他人！") : contents;
		String multimt = "[{\"mobile\":\"" + mobile + "\",\"content\":\"" + contents + "\"}]";
		buffer.append("systemSource=" + systemSource);
		buffer.append("&timestamp=" + timestamp);
		buffer.append("&multimt=" + URLEncoder.encode(multimt, "UTF-8"));
		buffer.append("&branch=" + redis.getString(Constants.BRANCH_OFFICE_CODE));// 分公司机构代码
		buffer.append("&loginNo=" + mobile);// 账户名
		// 获取短信服务信息
		@SuppressWarnings("rawtypes")
		Map map = (Map) redis.get(Constants.SYS_SERVICE_INFO);
		SysServiceInfo sysServiceInfo = (SysServiceInfo) map.get(CoreServiceEnum.SendMsg_URL.getUrlKey());
		// 组合请求实体
		HttpEntity<?> requestEntity = new HttpEntity<>(
				RequestUtils.genEncryptHttpHeaders(sysServiceInfo.getUserName(), sysServiceInfo.getPassword()));
		// 请求地址
		String url = sysServiceInfo.getUrl() + "?" + buffer.toString();
		// 请求并返回信息
		MessageResponseDto responseDto = restTemplate.postForObject(url, requestEntity, MessageResponseDto.class);

		// 日志
		TbSpSaleLog saleLog = new TbSpSaleLog();
		saleLog.setOperation(TbSpSaleLog.SMS_MSG);
		saleLog.setMark("发送短信");
		saleLog.setRequestTime(new Date());
		saleLog.setRequestData("url: " + url);
		saleLog.setResponseData(JSON.toJSONString(responseDto));
		spSaleLogDao.save(saleLog);

		// 短信发送结果描述
		sr.setResult(responseDto.getResultMsg());
		// 发送成功
		if ("0".equals(responseDto.getResult())) {
			sr.setSuccess();
			if (isSendCode) {
				HashMap<String, String> verifyCode = Maps.newHashMap();
				verifyCode.put("mobile", mobile);
				verifyCode.put("code", code);
				verifyCode.put("time", DateUtil.getStringDate2());// yyyy-mm-dd hh:mm:ss.sss

				// 本批短信的唯一编号,result非0时，custid为空
				verifyCode.put("custid", responseDto.getCustid());
				// 平台流水号：非0，64位整型，对应Java和C#的long，不可用int解析。result非0时，msgid为0
				verifyCode.put("msgid", responseDto.getMsgid());

				redis.setWithExpireTime(request.getSession().getId(), Constants.REDIS_PHONE_VERIFY_CODE, verifyCode, Constants.SMS_VALIDATE_OUTTIME);
			}
			log.info(typeName + "_发送短信至:{},发送内容:{},发送结果:{}", mobile, contents, JSON.toJSONString(responseDto));
			System.out.println(typeName + "_发送短信至:" + mobile + ",发送内容:" + contents + ",发送结果:" + JSON.toJSONString(responseDto));
		} else {
			sr.setResult("获取太快哦,请稍后再试");
		}
		return sr;
	}

	@Override
	public ServiceResult phoneNumCheckForLogin(ServiceResult sr, String mobile) {
		sr.setAppFail("手机号码无效");
		if (StringUtils.isBlank(mobile)) {
			sr.setResult("手机号码为空");
			return sr;
		}
		if (mobile.matches(Constants.REGEX_NUMBER_PHONE)) {
			sr.setResult("手机号码正确");
			sr.setSuccess();
			return sr;
		} else {
			sr.setResult("手机号码错误");
		}
		return sr;
	}

	@Override
	public ServiceResult phoneNumCheck(ServiceResult sr, String mobile) {
		sr.setAppFail("手机号码无效");

		if (StringUtils.isBlank(mobile)) {
			sr.setResult("手机号码为空");
			return sr;
		}
		if (mobile.length() != 11) {
			sr.setResult("手机号码长度为11位");
			return sr;
		}
		if (mobile.matches(Constants.REGEX_NUMBER_PHONE)) {
			sr.setResult("手机号码正确");
			sr.setSuccess();
			return sr;
		} else {
			sr.setResult("手机号码错误");
		}
		// // 检验手机号段
		// String[] numbers = Constants.MOBILE_BEFOR_THREE_NOS.split(",");
		// if (numbers.length > 0) {
		// // 当前手机前3位
		// String str = mobile.substring(0, 3);
		// for (String area : numbers) {
		// if (StringUtils.isNotBlank(area) && area.trim().length() == 3 &&
		// area.trim().equals(str)) {
		// map.put("msg", "手机号正确");
		// map.put("state", ServiceResult.STATE_SUCCESS);
		// break;
		// } else {
		// map.put("msg", "手机号段不正确");
		// }
		// }
		// }
		return sr;
	}

	@Override
	public String createCode() {
		StringBuffer code = new StringBuffer();
		while (true) {
			for (int i = 1; i <= Constants.REGEX_NUMBER_CODE_LENGTH; i++) {
				code.append(new Random().nextInt(9));
			}
			if (code.toString().contains("4")) {
				code = new StringBuffer();
				continue;
			} else {
				break;
			}
		}
		return code.toString();
	}

	//
	// Map<String, SysServiceInfo> mapSysServiceInfo = (Map<String, SysServiceInfo>)
	// redis
	// .get(Constants.SYS_SERVICE_INFO);
	// SysServiceInfo sysServiceInfo = null;//
	// mapSysServiceInfo.get(CoreServiceEnum.GET_MESSAGE_URL.getUrlKey());
	// String sUrl = sysServiceInfo.getUrl();
	//
	// URL url = new URL(sUrl);
	// conn = (HttpURLConnection) url.openConnection();
	// conn.setDoOutput(true);
	// conn.setDoInput(true);
	// conn.setRequestMethod("POST");
	// conn.setConnectTimeout(30000);
	// conn.setReadTimeout(30000);
	//
	// DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());
	//
	// StringBuilder sb = new StringBuilder(
	// "<?xml version=\"1.0\"
	// encoding=\"UTF-8\"?><PACKET><HEAD><REQUEST_CODE>SMS</REQUEST_CODE><USER>test</USER><PASSWORD>test</PASSWORD></HEAD><BODY><BASE_PART><SendTarget>");
	// sb.append(mobile);
	// sb.append("</SendTarget><SMContent>");
	// // sb.append(msgcontant).append("为本次验证码，请在有效时间内验证确认，谢谢！");
	// sb.append("</SMContent><RCompleteTimeBegin>");
	// sb.append(DateUtil.dateFromat(new Date()));
	// sb.append("</RCompleteTimeBegin><RCompleteTimeEnd>");
	// sb.append(DateUtil.dateFromat(new Date()));
	// sb.append(
	// "</RCompleteTimeEnd><RCompleteHourBegin>600</RCompleteHourBegin><RCompleteHourEnd>1080</RCompleteHourEnd><SendTargetType>3</SendTargetType><Priority>5</Priority><Remark>LifeAgent</Remark></BASE_PART></BODY></PACKET>");
	//
	// log.info("【用户验证】请求报文：" + sb.toString());
	//
	// outStream.writeUTF(sb.toString());
	// outStream.close();
	// outStream = null;
	// DataInputStream inStream = new DataInputStream(conn.getInputStream());
	// StringBuilder xml = new StringBuilder(256);
	// do
	// xml.append(inStream.readUTF());
	// while (xml.indexOf("</PACKET>") < 0);
	//
	// log.info("【用户验证】请求响应报文：" + xml.toString());
}
